<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Filter Badge Component</title>
  <style>
    * {
      box-sizing: border-box;
    }
    
    body {
      font-family: Helvetica, Arial, sans-serif;
      margin: 0;
      padding: 20px;
      background-color: #f5f5f5;
      color: #333;
    }
    
    .filters-container {
      background-color: white;
      border-radius: 8px;
      padding: 20px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }
    
    .filter-badges {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      margin-bottom: 20px;
    }
    
    .filter-badge {
      display: flex;
      align-items: center;
      background-color: #f0f7ff;
      border: 1px solid #cce0ff;
      border-radius: 20px;
      overflow: hidden;
      height: 36px;
      transition: box-shadow 0.2s ease;
    }
    
    .filter-badge:hover {
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }
    
    .filter-badge-part {
      padding: 0 10px;
      height: 100%;
      display: flex;
      align-items: center;
      cursor: pointer;
    }
    
    .filter-badge-column {
      font-weight: 500;
      background-color: #e0f0ff;
      border-right: 1px solid #cce0ff;
      min-width: auto;
      width: auto;
      position: relative;
      white-space: nowrap;
      padding: 0 12px;
    }
    
    .filter-badge-operator {
      min-width: 40px;
      width: auto;
      padding: 0 8px;
      justify-content: center;
      background-color: #f0f7ff;
      border-right: 1px solid #cce0ff;
      position: relative;
      white-space: nowrap;
    }
    
    .filter-badge-value {
      padding: 0 12px;
      background-color: #f8faff;
      flex-grow: 1; /* Allow this to grow to take available space */
    }
    
    .filter-badge-remove {
      width: 28px;
      justify-content: center;
      font-size: 16px;
      color: #999;
      background-color: #f8faff;
      border-left: 1px solid #cce0ff;
      transition: background-color 0.2s ease;
      margin-left: auto; /* Push to the right end */
    }
    
    .filter-badge-remove:hover {
      background-color: #ffeeee;
      color: #ff5555;
    }
    
    .dropdown-select {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      opacity: 0;
      cursor: pointer;
    }
    
    .add-filter-badge {
      display: inline-flex;
      align-items: center;
      background-color: #f0f7ff;
      border: 1px dashed #99c2ff;
      border-radius: 20px;
      height: 36px;
      padding: 0 15px;
      color: #3377cc;
      cursor: pointer;
      transition: background-color 0.2s ease;
      width: auto;
    }
    
    .add-filter-badge:hover {
      background-color: #e0f0ff;
    }
    
    .add-filter-badge span {
      font-size: 20px;
      margin-right: 5px;
    }
    
    .filter-popover {
      position: absolute;
      background: white;
      border-radius: 8px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
      padding: 15px;
      z-index: 100;
      min-width: 220px;
      display: none;
    }
    
    .filter-popover.active {
      display: block;
    }
    
    .filter-popover:after {
      content: '';
      position: absolute;
      top: -8px;
      left: 20px;
      width: 16px;
      height: 16px;
      background: white;
      transform: rotate(45deg);
      box-shadow: -2px -2px 4px rgba(0, 0, 0, 0.05);
    }
    
    .popover-title {
      font-weight: 500;
      margin-bottom: 10px;
      font-size: 14px;
      color: #666;
    }
    
    input[type="text"] {
      width: 100%;
      padding: 8px 12px;
      border: 1px solid #ddd;
      border-radius: 4px;
      font-family: Helvetica, Arial, sans-serif;
      font-size: 16px;
    }
    
    .popover-actions {
      display: flex;
      justify-content: flex-end;
      margin-top: 10px;
      gap: 8px;
    }
    
    button {
      padding: 8px 12px;
      border: none;
      border-radius: 4px;
      font-family: Helvetica, Arial, sans-serif;
      font-size: 14px;
      cursor: pointer;
    }
    
    button.primary {
      background-color: #3377cc;
      color: white;
    }
    
    button.secondary {
      background-color: #f0f0f0;
      color: #666;
    }

    /* Keyboard focus styles */
    .filter-badge-part:focus-within {
      outline: 2px solid #4d90fe;
      outline-offset: -2px;
    }
    
    .add-filter-badge:focus {
      outline: 2px solid #4d90fe;
      outline-offset: 2px;
    }
    
    .dropdown-select:focus {
      outline: none; /* Remove default outline as we're styling the parent */
    }
    
    button:focus {
      outline: 2px solid #4d90fe;
      outline-offset: 2px;
    }
    
    input[type="text"]:focus {
      outline: 2px solid #4d90fe;
      border-color: #4d90fe;
    }

    @media (max-width: 600px) {
      .filter-badges {
        flex-direction: column;
        align-items: flex-start;
      }
      
      .filter-badge {
        width: 100%;
        justify-content: space-between; /* Ensure space distribution */
      }
      
      .filter-badge-value {
        flex-grow: 1; /* Allow value to grow */
      }
      
      .filter-badge-remove {
        margin-left: auto; /* Always push to end */
      }
    }
  </style>
</head>
<body>
<h1>Keyboard accessible filter prototype</h1>
  <div class="filters-container">
    <div class="filter-badges" id="filterBadges">
      <!-- Filter badges will be generated here -->
    </div>
    
    <div class="add-filter-badge" id="addFilterBadge" tabindex="0" role="button" aria-label="Add filter">
      <span>+</span> Add filter
    </div>
  </div>
  
  <!-- Value Popover -->
  <div class="filter-popover" id="valuePopover">
    <div class="popover-title">Enter value</div>
    <input type="text" id="valueInput" aria-label="Filter value">
    <div class="popover-actions">
      <button class="secondary" id="cancelButton" onclick="closeValuePopover()">Cancel</button>
      <button class="primary" id="applyValueButton">Apply</button>
    </div>
  </div>
  
  <script type="module">
// Initial filter data
const filters = [
  {
    id: 1,
    column: 'award_number',
    operator: 'gt',
    value: '5'
  },
  {
    id: 2,
    column: 'rowid',
    operator: 'exact',
    value: '4'
  }
];

// Current active filter being edited
let activeFilter = null;
let lastFocusedElementId = null; // Track the last focused element

// DOM references
const filterBadgesContainer = document.getElementById('filterBadges');
const addFilterBadge = document.getElementById('addFilterBadge');
const valuePopover = document.getElementById('valuePopover');

// Operator display mapping
const operatorDisplay = {
  'exact': '=',
  'not': '!=',
  'contains': 'contains',
  'notcontains': 'does not contain',
  'endswith': 'ends with',
  'startswith': 'starts with',
  'gt': '>',
  'gte': '≥',
  'lt': '<',
  'lte': '≤',
  'like': 'like',
  'notlike': 'not like',
  'glob': 'glob',
  'in': 'in',
  'notin': 'not in',
  'arraycontains': 'array contains',
  'arraynotcontains': 'array does not contain',
  'date': 'date',
  'isnull__1': 'is null',
  'notnull__1': 'is not null',
  'isblank__1': 'is blank',
  'notblank__1': 'is not blank'
};

// Initialize the UI
function initUI() {
  renderFilterBadges();
  setupEventListeners();
}

// Render filter badges
function renderFilterBadges() {
  // Save focus state before re-rendering
  saveCurrentFocus();
  
  filterBadgesContainer.innerHTML = '';
  
  filters.forEach(filter => {
    const badge = document.createElement('div');
    badge.className = 'filter-badge';
    badge.dataset.id = filter.id;
    
    // Create unique identifiers for each focusable element
    const columnId = `filter-column-${filter.id}`;
    const operatorId = `filter-operator-${filter.id}`;
    const valueId = `filter-value-${filter.id}`;
    const removeId = `filter-remove-${filter.id}`;
    
    badge.innerHTML = `
      <div id="${columnId}" class="filter-badge-part filter-badge-column" data-id="${filter.id}">
        ${filter.column}
        <select class="dropdown-select" data-id="${filter.id}" tabindex="0" aria-label="Select column">
          <option value="">- remove filter -</option>
          <option value="rowid">rowid</option>
          <option value="award_number">award_number</option>
          <option value="group">group</option>
          <option value="award_title">award_title</option>
          <option value="type">type</option>
          <option value="keywords">keywords</option>
        </select>
      </div>
      <div id="${operatorId}" class="filter-badge-part filter-badge-operator" data-id="${filter.id}">
        ${operatorDisplay[filter.operator] || filter.operator}
        <select class="dropdown-select" data-id="${filter.id}" tabindex="0" aria-label="Select operator">
          <option value="exact">=</option>
          <option value="not">!=</option>
          <option value="contains">contains</option>
          <option value="notcontains">does not contain</option>
          <option value="endswith">ends with</option>
          <option value="startswith">starts with</option>
          <option value="gt">&gt;</option>
          <option value="gte">≥</option>
          <option value="lt">&lt;</option>
          <option value="lte">≤</option>
          <option value="like">like</option>
          <option value="notlike">not like</option>
          <option value="glob">glob</option>
          <option value="in">in</option>
          <option value="notin">not in</option>
          <option value="arraycontains">array contains</option>
          <option value="arraynotcontains">array does not contain</option>
          <option value="date">date</option>
          <option value="isnull__1">is null</option>
          <option value="notnull__1">is not null</option>
          <option value="isblank__1">is blank</option>
          <option value="notblank__1">is not blank</option>
        </select>
      </div>
      <div id="${valueId}" class="filter-badge-part filter-badge-value" data-id="${filter.id}" tabindex="0" role="button" aria-label="Edit value">${filter.value}</div>
      <div id="${removeId}" class="filter-badge-part filter-badge-remove" data-id="${filter.id}" tabindex="0" role="button" aria-label="Remove filter">×</div>
    `;
    
    filterBadgesContainer.appendChild(badge);
    
    // Set the select values to match the current filter
    const columnSelect = badge.querySelector('.filter-badge-column select');
    const operatorSelect = badge.querySelector('.filter-badge-operator select');
    
    if (columnSelect) columnSelect.value = filter.column;
    if (operatorSelect) operatorSelect.value = filter.operator;
  });
  
  // Restore focus after re-rendering
  restoreFocus();
}

// Save current focused element
function saveCurrentFocus() {
  const activeElement = document.activeElement;
  if (activeElement && activeElement.closest('.filter-badge')) {
    lastFocusedElementId = activeElement.id || 
                           activeElement.closest('[id]')?.id ||
                           activeElement.closest('[data-id]')?.dataset.id;
  }
}

// Restore focus to the previously focused element
function restoreFocus() {
  if (lastFocusedElementId) {
    const elementToFocus = document.getElementById(lastFocusedElementId);
    if (elementToFocus) {
      setTimeout(() => {
        elementToFocus.focus();
      }, 0);
    }
  }
}

// Setup event listeners
function setupEventListeners() {
  // Track focus events
  document.addEventListener('focusin', (e) => {
    if (e.target.closest('.filter-badge') || e.target.closest('.add-filter-badge')) {
      const element = e.target.closest('[id]');
      if (element) {
        lastFocusedElementId = element.id;
      }
    }
  });

  // Add filter badge
  addFilterBadge.addEventListener('click', () => {
    const newFilter = {
      id: Date.now(),
      column: 'rowid',
      operator: 'exact',
      value: ''
    };
    
    filters.push(newFilter);
    renderFilterBadges();
    
    // Auto-focus the column select of the new filter
    setTimeout(() => {
      const columnId = `filter-column-${newFilter.id}`;
      const columnElement = document.getElementById(columnId);
      if (columnElement) {
        const columnSelect = columnElement.querySelector('select');
        if (columnSelect) {
          columnSelect.focus();
          lastFocusedElementId = columnId;
        }
      }
    }, 50);
  });
  
  // Add keyboard support for "add filter" button
  addFilterBadge.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      addFilterBadge.click();
    }
  });
  
  // Handle column select change
  document.addEventListener('change', (e) => {
    if (e.target.classList.contains('dropdown-select') && e.target.parentElement.classList.contains('filter-badge-column')) {
      const id = parseInt(e.target.dataset.id);
      const filter = filters.find(f => f.id === id);
      if (filter) {
        filter.column = e.target.value;
        
        // If empty column is selected, remove the filter
        if (!filter.column) {
          removeFilter(id);
          return;
        }
        
        // Update the display text without full re-render
        e.target.parentElement.childNodes[0].textContent = filter.column;
      }
    }
  });
  
  // Handle operator select change
  document.addEventListener('change', (e) => {
    if (e.target.classList.contains('dropdown-select') && e.target.parentElement.classList.contains('filter-badge-operator')) {
      const id = parseInt(e.target.dataset.id);
      const filter = filters.find(f => f.id === id);
      if (filter) {
        filter.operator = e.target.value;
        
        // Update the display text without full re-render
        e.target.parentElement.childNodes[0].textContent = operatorDisplay[filter.operator] || filter.operator;
      }
    }
  });
  
  // Filter badge value click
  document.addEventListener('click', (e) => {
    if (e.target.classList.contains('filter-badge-value')) {
      const id = parseInt(e.target.dataset.id);
      activeFilter = filters.find(f => f.id === id);
      showValuePopover(e.target);
    }
  });
  
  // Filter badge value keyboard
  document.addEventListener('keydown', (e) => {
    if (e.target.classList.contains('filter-badge-value')) {
      // Open popover on Enter/Space
      if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        const id = parseInt(e.target.dataset.id);
        activeFilter = filters.find(f => f.id === id);
        showValuePopover(e.target);
      }
      // Open popover on any typing (except navigation keys)
      else if (
        e.key.length === 1 || // Single character keys
        e.key === 'Backspace' || 
        e.key === 'Delete'
      ) {
        e.preventDefault();
        const id = parseInt(e.target.dataset.id);
        activeFilter = filters.find(f => f.id === id);
        showValuePopover(e.target);
        
        // After popover is open, forward the typed character to the input
        setTimeout(() => {
          const valueInput = document.getElementById('valueInput');
          if (valueInput) {
            if (e.key.length === 1) {
              // For printable characters, set the input value to that character
              valueInput.value = e.key;
              // Position cursor at the end
              valueInput.selectionStart = valueInput.selectionEnd = valueInput.value.length;
            } else if (e.key === 'Backspace' || e.key === 'Delete') {
              // For delete keys, start with empty value
              valueInput.value = '';
            }
          }
        }, 10);
      }
    }
  });
  
  // Filter badge remove click
  document.addEventListener('click', (e) => {
    if (e.target.classList.contains('filter-badge-remove')) {
      const id = parseInt(e.target.dataset.id);
      removeFilter(id);
    }
  });
  
  // Filter badge remove keyboard
  document.addEventListener('keydown', (e) => {
    if ((e.key === 'Enter' || e.key === ' ') && e.target.classList.contains('filter-badge-remove')) {
      e.preventDefault();
      const id = parseInt(e.target.dataset.id);
      removeFilter(id);
    }
  });
  
  // Apply value button
  document.getElementById('applyValueButton').addEventListener('click', () => {
    applyValueChange();
  });
  
  // Cancel button
  document.getElementById('cancelButton').addEventListener('click', () => {
    closeValuePopover();
  });
  
  // Enter key in value input
  document.getElementById('valueInput').addEventListener('keydown', (e) => {
    if (e.key === 'Enter') {
      e.preventDefault();
      applyValueChange();
    }
    if (e.key === 'Escape') {
      closeValuePopover();
    }
  });
  
  // Close popovers when clicking outside
  document.addEventListener('click', (e) => {
    const isPopover = e.target.closest('.filter-popover');
    const isFilterValue = e.target.classList.contains('filter-badge-value');
    
    if (!isPopover && !isFilterValue) {
      closeValuePopover();
    }
  });
}

// Show value popover
function showValuePopover(targetElement) {
  // If popover is already active, don't reinitialize
  if (valuePopover.classList.contains('active')) {
    return;
  }
  
  const rect = targetElement.getBoundingClientRect();
  
  // Calculate available space on the right
  const viewportWidth = window.innerWidth;
  const rightSpace = viewportWidth - rect.left - window.scrollX;
  
  // Check if there's enough space to the right
  if (rightSpace < 250) { // 250px is approximate popover width
    // Not enough space on right, position to the left of target
    valuePopover.style.left = `${Math.max(rect.left + window.scrollX - 200, 10)}px`;
  } else {
    // Enough space on right, position at target left
    valuePopover.style.left = `${rect.left + window.scrollX}px`;
  }
  
  valuePopover.style.top = `${rect.bottom + window.scrollY + 10}px`;
  
  // Set the current value
  const valueInput = document.getElementById('valueInput');
  if (activeFilter && activeFilter.value) {
    valueInput.value = activeFilter.value;
  } else {
    valueInput.value = '';
  }
  
  valuePopover.classList.add('active');
  valueInput.focus();
}

// Close value popover
function closeValuePopover() {
  valuePopover.classList.remove('active');
  
  // Return focus to the value element that opened it
  if (activeFilter) {
    const valueId = `filter-value-${activeFilter.id}`;
    const valueElement = document.getElementById(valueId);
    if (valueElement) {
      valueElement.focus();
      lastFocusedElementId = valueId;
    }
  }
}

// Apply value change
function applyValueChange() {
  const valueInput = document.getElementById('valueInput');
  if (activeFilter) {
    activeFilter.value = valueInput.value;
    
    // Update the display text without full re-render
    const valueId = `filter-value-${activeFilter.id}`;
    const valueElement = document.getElementById(valueId);
    if (valueElement) {
      valueElement.textContent = activeFilter.value;
      closeValuePopover();
      valueElement.focus();
      lastFocusedElementId = valueId;
    } else {
      closeValuePopover();
    }
  }
}

// Remove filter
function removeFilter(id) {
  const index = filters.findIndex(f => f.id === id);
  if (index !== -1) {
    // Find next filter to focus on
    let nextFocusFilter;
    if (index < filters.length - 1) {
      nextFocusFilter = filters[index + 1];
    } else if (index > 0) {
      nextFocusFilter = filters[index - 1];
    }
    
    // Remove the filter
    filters.splice(index, 1);
    
    // Render and set focus
    renderFilterBadges();
    
    // If there are no filters left, focus the add button
    if (filters.length === 0) {
      addFilterBadge.focus();
      lastFocusedElementId = addFilterBadge.id;
    } 
    // Otherwise focus a related filter
    else if (nextFocusFilter) {
      const columnId = `filter-column-${nextFocusFilter.id}`;
      const columnElement = document.getElementById(columnId);
      if (columnElement) {
        columnElement.focus();
        lastFocusedElementId = columnId;
      }
    }
  }
}

// Initialize the UI
initUI();

// Expose for the cancel button
window.closeValuePopover = closeValuePopover;
  </script>
</body>
</html>
